API GatewayにLambda(Node.js)でBASIC認証かける
API Gatewayに対して、Lambda オーソライザーという機能を使ってBASIC認証をかけてみました。個人的ハマりポイントもあったので、備忘録的に書いておきます。
前提
- Lambda関数のランタイムはすべて「Node.js 12.x」を選択します
- API GatewayはREST APIタイプを利用します
まずはLambdaでレスポンス作成
Lambda関数を「一から作成」でランタイム「Node.js 12.x」を選択して作成します。
すると下記のようなコードで関数が生成されます。
exports.handler = async (event) => { // TODO implement const response = { statusCode: 200, body: JSON.stringify('Hello from Lambda!'), }; return response; };
これでいったんデプロイ。
API Gatewayの準備
1.APIの作成
先ほど作ったlambda関数をAPI Gatewayに紐付けます。
まずはAPI Gatewayコンソールに移動して、APIを新規作成します。
タイプは「REST API」を選択。
2.メソッドの作成
左サイドバー[リソース]-[アクション]-[メソッドの作成]から[GET]メソッドを作成し、バックエンドとして先ほど作成したLambda関数を指定します。
3.ステージの作成とデプロイ
API Gatewayではステージ機能を使って環境ごとに分けて管理ができます。
左サイドバー[リソース]-[アクション]-[API のデプロイ]から、ステージ名[dev]でデプロイします。
[URL の呼び出し]としてエンドポイントURLが生成されますので、アクセスすると「"Hello from Lambda!"」の文字が返ってきます。
% curl https://voubot37fi.execute-api.eu-central-1.amazonaws.com/dev
{"statusCode":200,"body":"\"Hello from Lambda!\""}%
簡単なAPI Gateway - Lambdaアプリは準備ができました。
BASIC認証用Lambda実装
Lambdaオーソライザーを利用してBASIC認証を実装します。
Lambdaコンソールに移動し、新たにLambda関数を作成します。
こちらもランタイムはNode.js 12.xです。
exports.handler = function (event, context, callback) { var authorizationHeader = event.headers.authorization if (!authorizationHeader) return callback('Unauthorized') var encodedCreds = authorizationHeader.split(" ")[1] var plainCreds = (new Buffer(encodedCreds, 'base64')).toString().split(':') var username = plainCreds[0] var password = plainCreds[1] if (!(username === 'admin' && password === 'password')) return callback('Unauthorized') var authResponse = buildAllowAllPolicy(event, username) callback(null, authResponse) } function buildAllowAllPolicy (event, principalId) { const policy = { principalId: principalId, policyDocument: { Version: '2012-10-17', Statement: [ { Action: 'execute-api:Invoke', Effect: 'Allow', Resource: event.methodArn } ] } } return policy }
同様の実装例を他に検索してみると、event.headers.authorization
が大文字['Authorization']になっている記事が多かったのですが、自分の場合それだとブラウザのBASIC認証が通らなかったので、小文字にしています(AWSチュートリアルでも小文字ですし)。
この関数もデプロイします。
API Gatewayでの設定
レスポンスヘッダーの追加
API Gatewayコンソールに移動し、左サイドバー[ゲートウェイのレスポンス]-[権限がありません]を選択し、レスポンスヘッダとして、
・レスポンスヘッダー: WWW-Authenticate
・値: 'Basic'
を追加します(値はシングルクォーテーション込みなのを忘れずに)。
オーソライザーの作成
次に、左サイドバー[オーソライザー]-[新しいオーソライザーの作成]でオーソライザーを作成します。
先ほど作成したBASIC認証用のLambda関数をセットし、[Lambda イベントペイロード]は[リクエスト]、[IDソース]としてヘッダーに[Authorization]を記述します。[認可のキャッシュ]もチェックを外します。
先述のAuthorization大文字小文字問題と異なり、こちらは大文字'Authorization'でもBASIC認証が通るのですが、Lambdaのほうを小文字にしている関係で、こちらも小文字にしないと次に行うAPI Gateway側のテストが通りません。
作成後、[テスト]からAPI Gatewayのテストが行えるのでやってみます。
先ほどのBASIC認証用Lambda関数でセットしたユーザー名とパスワードを繋げた状態でBase64エンコードして、リクエストパラメータにセットします。
レスポンス200が返ってきているので成功しました。
メソッドリクエストにオーソライザー追加
左サイドバー[リソース]-[GET]-[メソッドリクエスト]を編集し、[認可]に先ほど作成したオーソライザーを追加します。
これで準備ができたので、APIをデプロイし、再度URLにアクセスしてみます。
無事BASIC認証のポップアップが現れました。
参考URL
https://dev.classmethod.jp/articles/http-api-support-iam-and-lambda-authorizer/
https://dev.classmethod.jp/articles/getting-start-api-gateway/
https://qiita.com/diaphragm/items/b87d700ef36fa62c1aa8
https://medium.com/@Da_vidgf/http-basic-auth-with-api-gateway-and-serverless-5ae14ad0a270